import {
  Id,
  Ig,
  accessify,
  soList,
  toAssignment,
} from './entry'

import {
  Constant,
  Identifier,
  Ignore,
  Member,
  Multivalued,
  Pending,
  Sign,
  Translator,
} from '../ast/types'

import sign, {
  isLiteral
} from '../ast/sign'

import decode from './decode'

const
  IGNORE  = sign['_'],
  COMMA   = sign[','],
  EQUAL   = sign['='],
  IOTA    = sign.iota,
  IDENT   = sign.ident

function leftValue (s, sign) {
  return sign === IGNORE &&  Ig(s)
    || sign === IDENT && Id(s)
    || s.fail(sign)
}

function member (object, property) {
  return property && new Member(object, property)
    || object
}

function transify (s) {
  if (!s.sps().at()) return null
  let start = s.start

  return new Translator(member(s.beId(), s.dot() && s.asId()))
    .start(start).end(s.end)
}

function beMultivalued (s, x) {
  let
    node = new Multivalued().start(x.loc.start),
    sign = s.beMore()

  while (sign) {
    node.targets.append(leftValue(s, sign))
    sign = s.beMore()
    if (sign === EQUAL) break
  }

  node.operator = EQUAL

  sign = s.beMore()
  while (sign) {
    [x, sign] = soList(s, sign)
    node.append(x)

    if (sign === COMMA) sign = s.beMore()
  }
  return node.end(x.loc.end)
}

function token (s, sign) {
  return sign === IDENT && Id(s)
    || isLiteral(sign) && decode(s, sign)
    || new Sign(sign).start(s.start).end(s.end)
}

export default function (s) {
  let
    start = s.start,
    node = new Constant(accessify(s.text()), transify(s))
      .start(start),
    sign = s.beNext()

  s.lead()

  if (node.translator) {
    while (sign) {
      let x = new Pending(leftValue(s, sign))

      sign = s.beMore()
      if (sign !== EQUAL) s.fail(sign)

      sign = s.beMore()
      while (sign) {
        x.append(token(s, sign))
        sign = s.soMore()
      }

      node.append(x)
      sign = s.isAlign() && s.right() || 0
    }
    return node.end(s.end)
  }

  s.nothis  = true
  s.nosuper = true
  s.iotable = true
  while (sign) {
    let x, left  = leftValue(s, sign)

    sign = s.beMore()

    if (sign === EQUAL)
    {
      [x, sign] = soList(s, s.beMore())
      s.beZero(sign)
      x = toAssignment(s, left, EQUAL, x)
    }
    else if (sign === COMMA)
      x = beMultivalued(s, left)
    else
      s.fail(sign)

    node.append(x)
    sign = s.isAlign() && s.right() || 0
  }
  s.nothis  = false
  s.nosuper = false
  s.iotable = false

  return node.end(s.end)
}
